package com.base.data.elasticsearch.util;

import com.google.common.base.Throwables;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.base.data.elasticsearch.annotations.Field;
import com.base.data.elasticsearch.annotations.FieldType;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;

import java.io.Reader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class GsonUtil {

    public static final String DATEFORMAT_BASE = "yyyy-MM-dd HH:mm:ss";

    protected static final GsonBuilder INSTANCE = new GsonBuilder().setDateFormat(DATEFORMAT_BASE);

    static {
        INSTANCE.disableHtmlEscaping();
        INSTANCE.registerTypeAdapter(Date.class, (JsonSerializer<Date>) (date, type, jsonSerializationContext) -> {
                    if (Objects.isNull(date)) {
                        return new JsonPrimitive(Strings.EMPTY);
                    }
                    return new JsonPrimitive(new SimpleDateFormat(DATEFORMAT_BASE).format(date));
                })
                .registerTypeAdapter(Date.class, (JsonDeserializer<Date>) (json, typeOfT, context) -> {
                    JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
                    if (jsonPrimitive.isNumber()) {
                        long time = jsonPrimitive.getAsLong();
                        return new Date(time);
                    } else if (jsonPrimitive.isString()) {
                        String string = jsonPrimitive.getAsString();
                        try {
                            return new SimpleDateFormat(DATEFORMAT_BASE).parse(string);
                        } catch (ParseException e) {
                            System.out.println(Throwables.getStackTraceAsString(e));
                        }
                    }
                    return null;
                });
    }

    public static Gson create() {
        return INSTANCE.create();
    }

    public static String toString(Object o) {
        return create().toJson(o);
    }


    public static <T> T fromJson(String json, Class<T> clas) {
        return create().fromJson(json, clas);
    }


    public static String toJSONStringIgnoreTransient(Object object) {
        Gson mGson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
                    @Override
                    public boolean shouldSkipField(FieldAttributes f) {
                        return shouldSkipFieldMethod(f);
                    }
                    @Override
                    public boolean shouldSkipClass(Class<?> clazz) {
                        return false;
                    }
                })
                .serializeNulls().setDateFormat(DATEFORMAT_BASE).create();
        return mGson.toJson(object);
    }

    private static final JsonParser JSON_PARSER = new JsonParser();

    public static JsonObject parse(String json) {
        return JSON_PARSER.parse(json).getAsJsonObject();
    }

    public static JsonObject parse(Reader json) {
        return JSON_PARSER.parse(json).getAsJsonObject();
    }

    public static JsonObject parse(JsonReader json) {
        return JSON_PARSER.parse(json).getAsJsonObject();
    }

    public static JsonObject parse(Object object) {
        return JSON_PARSER.parse(object.toString()).getAsJsonObject();
    }

    private static boolean shouldSkipFieldMethod(FieldAttributes f){
        if(null != f.getAnnotation(javax.persistence.Transient.class) || null!= f.getAnnotation(Transient.class)) {
            return true;
        }
        if(null == f.getAnnotation(Field.class)) {
            if(null != f.getAnnotation(Id.class) || null != f.getAnnotation(javax.persistence.Id.class) || "id" == f.getName()){
                return false;
            }
            return true;
        }else {
            return false;
        }
    }

}
